﻿using System;
using System.CodeDom;
using System.Linq;
using AopCore;
using Roslyn.Compilers.CSharp;

namespace WeaverCustomTool
{
    public struct PropertyDeclaration
    {
        private readonly PropertyDeclarationSyntax propertySyntax;

        public PropertyDeclaration(PropertyDeclarationSyntax property)
        {
            this.propertySyntax = property;
        }

        public CodeMemberProperty InCodeTypeDeclaration(CodeTypeDeclaration type)
        {
            var codeTypeReference = propertySyntax.Type.ToTypeReference();
            var propertyNameToWeave = propertySyntax.Identifier.ValueText;
            var privateFieldName = propertyNameToWeave.ToCamelCase();
            
            var weavedField = new CodeMemberField
            {
                Name = privateFieldName,
                Type = codeTypeReference,
                Attributes = MemberAttributes.Private
            };

            var accessorToWeaveGet = propertySyntax.AccessorList.Accessors.FirstOrDefault(acc => acc.Keyword.Value.ToString() == "get");
            var accessorToWeaveSet = propertySyntax.AccessorList.Accessors.FirstOrDefault(acc => acc.Keyword.Value.ToString() == "set");
            var weavedProperty = new CodeMemberProperty
            {
                Name = propertyNameToWeave,
                Type = codeTypeReference,
                HasGet = accessorToWeaveGet != null,
                HasSet = accessorToWeaveSet != null,
                //Attributes = MemberAttributes.Public | MemberAttributes.Final
                Attributes = (MemberAttributes) 24578
            };

            // Mapping management
            var mapAttribute = propertySyntax.ManageAttributes().FirstOfType<MapAttribute>();
            new MappingPropertyDeclaration(weavedProperty, weavedField).ImplementCommandMappingOntype(mapAttribute, type);

            var codeFieldReference = new CodeFieldReferenceExpression(
                new CodeThisReferenceExpression(),
                weavedField.Name);

            if (weavedProperty.HasGet)
            {
                weavedProperty.GetStatements.Add(new CodeMethodReturnStatement(codeFieldReference));
            }

            if (weavedProperty.HasSet)
            {
                var fieldAssignmentStatment = new CodeAssignStatement(
                   codeFieldReference,
                   new CodePropertySetValueReferenceExpression());

                CodeStatementCollection setStatmentStream = null;

                if (propertySyntax.ManageAttributes().AnyOfType<SetterChecksChangesAttribute>())
                {
                    var oldValueCheckCondition = new CodeBinaryOperatorExpression(
                        codeFieldReference,
                        CodeBinaryOperatorType.IdentityInequality,
                        new CodePropertySetValueReferenceExpression());

                    var oldValueCheckConditionStatment = new CodeConditionStatement(oldValueCheckCondition, new CodeStatement[0]);
                    weavedProperty.SetStatements.Add(oldValueCheckConditionStatment);
                    setStatmentStream = oldValueCheckConditionStatment.TrueStatements;
                }
                else
                {
                    setStatmentStream = weavedProperty.SetStatements;
                }

                setStatmentStream.Add(fieldAssignmentStatment);
                if (propertySyntax.ManageAttributes().AnyOfType<NotifyPropertyChangedAttribute>())
                {
                    weavedProperty.ImplementINotifyPropertyChanged().ImplementINotifyPropertyChanged(setStatmentStream);
                }

                if(propertySyntax.ManageAttributes().AnyOfType<DeferedEditionAttribute>())
                {
                    new DeferEditionPropertyDeclaration(weavedProperty, type).ImplementDeferEdition();
                }
            }

            // Property comments
            var comments = propertySyntax.DescendantTrivia().FirstOrDefault(tr => tr.Kind == SyntaxKind.DocumentationCommentTrivia);
            var commentText = comments.ToFullString();
            if(!string.IsNullOrWhiteSpace(commentText))
            {
                var commentTextLines =
                    commentText
                    .Split(new[] {"\r\n", "\n"}, StringSplitOptions.None)
                    .Select(s => s.TrimStart('/', ' '))
                    .Where(s => !string.IsNullOrWhiteSpace(s)).ToList();
                weavedProperty.Comments.AddRange(commentTextLines.Select(l=> new CodeCommentStatement(l, true)).ToArray());
            }

            type.Members.Add(weavedField);
            type.Members.Add(weavedProperty);

            return weavedProperty;
        }
    }
}
